/*
* @Author: 窦子滨<zibin_5257@163.com>
* @Date: 2019-05-05 13:20
 */
package main

import (
	"context"
	"errors"
	"github.com/lanfengye2008/hprose-golang/rpc"
	"net/url"
	"reflect"
	"sync"
	"time"
)

var services = NewServiceMaper()

//微服务对象管理
type ServiceMaper interface {
	Add(k string, v Service)
	Delete(k string) (err error)
	Get(k string) (v Service, err error)
	All() (v map[string]Service)
	NewAndGet(name string) Service
}

func NewServiceMaper() ServiceMaper {
	return &ServiceManage{}
}

type ServiceManage struct {
	serviceMap sync.Map //[microName] Service
}

func (t *ServiceManage) Add(k string, v Service) {
	t.serviceMap.Store(k, v)
}

func (t *ServiceManage) Delete(k string) (err error) {
	v, err := t.Get(k)

	if  err == nil {
		v.CloseAll()
		t.serviceMap.Delete(k)
	}
	return
}

func (t *ServiceManage) Get(k string) (v Service, err error) {
	if n, ok := t.serviceMap.Load(k); ok {
		v = n.(Service)
	} else {
		err = errors.New("serviceManage not key")
	}
	return
}

func (t *ServiceManage) All() (v map[string]Service) {
	v = make(map[string]Service, 0)
	t.serviceMap.Range(func(key, value interface{}) bool {
		v[key.(string)] = value.(Service)
		return true
	})
	return
}

func (t *ServiceManage) NewAndGet(name string) Service {
	if s, err := t.Get(name); err == nil {
		return s
	}
	s := NewService(name, 0)
	t.Add(name, s)
	return s
}

type Service interface {
	AddConn(k string, v Conn)
	DeleteConn(k string)
	GetConn(k string) (v Conn, err error) //根据url和端口号获取链接  没有则创建一个并存储 url:端口号
	CloseAll()                            //关闭微服务所有的源连接
	ConnCount() int                       //源连接数量
	ConnList() (v map[string]Conn)
	NewAndGet(uri string) Conn
	InvokeSettings()*rpc.InvokeSettings
}

func NewService(name string, timeout time.Duration) Service {
	if timeout == 0 {
		timeout = time.Duration(30) * time.Minute
	}
	return &ServiceObj{
		invokeSettings:&rpc.InvokeSettings{
			Mode:        rpc.Raw,
			ResultTypes: []reflect.Type{reflect.TypeOf(([]byte)(nil))},
		},
		microName: name,
		timeout:   timeout,
	}
}

type ServiceObj struct {
	invokeSettings *rpc.InvokeSettings
	microName string   //微服务名称
	conns     sync.Map //[uri] Conn
	timeout   time.Duration //超时时间 源链接超时自动关闭
}

func (t *ServiceObj) InvokeSettings()*rpc.InvokeSettings{
	return t.invokeSettings
}

func (t *ServiceObj) AddConn(k string, v Conn) {
	t.conns.Store(k, v)
	v.ProLong()
}

func (t *ServiceObj) DeleteConn(k string) {
	//删除连接之前 需要进行关闭
	if v, err := t.GetConn(k); err == nil {
		v.Close()
		t.conns.Delete(k)
	}
}

func (t *ServiceObj) GetConn(k string) (v Conn, err error) {
	if n, ok := t.conns.Load(k); ok {
		v = n.(Conn)
		return v,nil
		v.ProLong()
	} else {
		err = errors.New("not key")
		return nil,err
	}
	return v,nil
}

func (t *ServiceObj) CloseAll() {
	t.conns.Range(func(key, value interface{}) bool {
		value.(Conn).Close()
		return true
	})
}

func (t *ServiceObj) ConnCount() (n int) {
	t.conns.Range(func(_, _ interface{}) bool {
		n++
		return true
	})
	return
}

func (t *ServiceObj) ConnList() (v map[string]Conn) {
	v = make(map[string]Conn)
	t.conns.Range(func(key, value interface{}) bool {
		v[key.(string)] = value.(Conn)
		return true
	})
	return
}

func (t *ServiceObj) NewAndGet(uri string) Conn{
	if s, err := t.GetConn(uri); err == nil {
		return s
	}
	s,_ := NewConn(t.microName, uri,t.timeout)
	t.AddConn(uri, s)
	return s
}


type Conn interface {
	Close()
	ProLong() //续期
	Client() rpc.Client
	Uri() string
}

func NewConn(name string, uri string, timeout time.Duration) (conn Conn, err error) {
	u, err := url.Parse(uri)
	if err != nil {
		return
	}

	var c rpc.Client

	switch u.Scheme {
	//case "http","https":
	case "tcp", "tcp4", "tcp6":
		a := rpc.NewTCPClient(uri)
		a.SetMaxPoolSize(50)
		c = a
	default:
		c = rpc.NewClient(uri)
	}

	//c.SetTimeout(time.Duration(10)*time.Second)
	c.SetTimeout(timeout)

	v := &ConnManage{
		microName: name,
		uri:       uri,
		conn:      c,
		timeout:   timeout,
		prolong:   make(chan int),
	}
	v.ctx, v.cancel = context.WithCancel(context.Background())

	go v.timeoutHandle()

	return v, nil
}

type ConnManage struct {
	microName string //微服务名称
	ctx       context.Context
	cancel    context.CancelFunc
	uri       string //连接地址+端口
	conn      rpc.Client
	timeout   time.Duration
	prolong   chan int //延长时间
}

func (t *ConnManage) Close() {
	t.conn.Close()
	t.cancel()
}

func (t *ConnManage) Client() rpc.Client{
	return t.conn
}

//删除服务管理对象中的连接
func (t *ConnManage) deleteService() {
	if v, err := services.Get(t.microName); err == nil {
		v.DeleteConn(t.uri)
	}
}

func (t *ConnManage) Uri() string{
	return t.uri
}

//超时自动关闭连接 预留
func (t *ConnManage) timeoutHandle() {
begin:
	for {
		select {
		case <-time.After(t.timeout):
			t.deleteService()
			break begin
		case <-t.prolong:
			t.conn.SetTimeout(t.timeout)
			continue
		case <-t.ctx.Done():
			break begin
		}
	}
}

//对超时进行续期
func (t *ConnManage) ProLong() {
	t.prolong <- 1
}
